/**
 * This file is part of gapro library.
 * 
 * gapro library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General
 * Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>
 * 
 * Copyright 2012 Vinay Penmatsa. All rights reserved.
 * 
 */
package org.stupidwhiz.gapro.core;

/**
 * 3D vector with 'double' type components
 * 
 * @author Vinay Penmatsa
 * @since 1.0
 */
public class Vector3d {

    // x-component
    private double x;

    // y-component
    private double y;

    // z-component
    private double z;

    /**
     * Initialzes a Vector3d with x, y, z component values of zero
     * 
     */
    public Vector3d() {
        this(0, 0, 0);
    }

    /**
     * Initialzes a Vector3d with given x,y,z components
     * 
     * @param x
     *            x-component
     * @param y
     *            y-component
     * @param z
     *            z-component
     */
    public Vector3d(double x, double y, double z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    /**
     * Adds the given vector to this vector
     * 
     * @param v
     * @return the resulting Vector3d after addition
     */
    public Vector3d add(Vector3d v) {
        return new Vector3d(x + v.x, y + v.y, z + v.z);
    }

    /**
     * Subtracts the given vector from this vector
     * 
     * @param v
     * @return the resulting Vector3d after subtraction
     */
    public Vector3d subtract(Vector3d v) {
        return new Vector3d(x - v.x, y - v.y, z - v.z);
    }

    /**
     * Multiplies this vector with the scalar parameter
     * 
     * @param f
     */
    public Vector3d multiply(double f) {
        return new Vector3d(x * f, y * f, z * f);
    }

    /**
     * Divides this vector with the scalar parameter
     * 
     * @param f
     */
    public Vector3d divide(double f) {
        if(f == 0)
            throw new IllegalArgumentException("parameter cannot be zero");
        return new Vector3d(x / f, y / f, z / f);
    }

    /**
     * Returns the magnitude of this vector
     * 
     * @param f
     */
    public double magnitude() {
        return Math.sqrt(x * x + y * y + z * z);
    }

    /**
     * Returns the dot product of this and the given vector
     * 
     * @param f
     */
    public double dot(Vector3d v) {
        return(x * v.x + y * v.y + z * v.z);
    }

    /**
     * Returns the dot product of this and the given vector
     * 
     * @param f
     */
    public Vector3d cross(Vector3d v) {
        return new Vector3d(y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x);
    }

    @Override
    public String toString() {
        return "Vector3dDouble [x=" + x + ", y=" + y + ", z=" + z + "]";
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        long temp;
        temp = Double.doubleToLongBits(x);
        result = prime * result + (int) (temp ^ (temp >>> 32));
        temp = Double.doubleToLongBits(y);
        result = prime * result + (int) (temp ^ (temp >>> 32));
        temp = Double.doubleToLongBits(z);
        result = prime * result + (int) (temp ^ (temp >>> 32));
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if(this == obj)
            return true;
        if(obj == null)
            return false;
        if(getClass() != obj.getClass())
            return false;

        Vector3d other = (Vector3d) obj;
        if(Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x))
            return false;
        if(Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y))
            return false;
        if(Double.doubleToLongBits(z) != Double.doubleToLongBits(other.z))
            return false;
        return true;
    }

}
